package scanning;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Timer;
import java.util.TimerTask;

import javax.swing.JOptionPane;

import simulator.Log;
import simulator.Phrases;
import keyboard.Keyboard;
import keyboard.Keyboard.KeyboardType;
import dictionary.Dictionary;

public class ScanningSimulator {
	private static final int DWELL_TIME = 1000;
	
	private ScanningKeyboard simulator;
	private Timer timer;
	
	private Dictionary dictionary;
	private Keyboard keyboard;
	private Phrases phrases;
	private String input;
	
	private Log log;
	
	public ScanningSimulator() {
		String userName = "user" + System.currentTimeMillis();
		String scene = "scanning";
		Object[] keyboardOptions = {"dynamic", "5keys"};
		int keyboardIndex = JOptionPane.showOptionDialog(null, "please select a keyboard:", "Keyboard", JOptionPane.DEFAULT_OPTION, JOptionPane.QUESTION_MESSAGE, null, keyboardOptions, keyboardOptions[0]);
		if (keyboardIndex == -1)
			return;
		String technique = (String)keyboardOptions[keyboardIndex];
		
		dictionary = new Dictionary("corpus.txt", 5000);
		keyboard = new Keyboard();
		keyboard.SetDictionary(dictionary);
		if (keyboardIndex == 0) {
			keyboard.SetKeyboardType(KeyboardType.UsingScanning);
		} else {
			keyboard.SetKeyboardType(KeyboardType.UsingScanning5);
		}
		
		phrases = new Phrases();
		phrases.input("phrases.txt");
		
		log = new Log(userName + "_" + scene + "_" + technique);
		log.log("session " + phrases.getSession());
		JOptionPane.showMessageDialog(null, "start?");
		log.log("phrase " + phrases.getPhrase());
		log.log("start");
		
		simulator = new ScanningKeyboard(log);
		simulator.setListener(this);
		
		simulator.addKeyListener(new KeyAdapter() {
			@Override
			public void keyPressed(KeyEvent event) {
				if (event.getKeyCode() == KeyEvent.VK_SPACE) {
					simulator.accept();
					resetTimer();
				}
				if (event.getKeyCode() == KeyEvent.VK_ENTER) {
					if (!input.equals("")) {
						phrases.passPhrase();
						if (phrases.experimentFinished()) {
							JOptionPane.showMessageDialog(null, "experiment finish.");
							System.exit(0);
						} else if (phrases.sessionFinished()) {
							JOptionPane.showMessageDialog(null, "session" + (phrases.getSession() - 1) + " finish.");
							log.log("session " + phrases.getSession());
						}
						log.log("phrase " + phrases.getPhrase());
						JOptionPane.showMessageDialog(null, "start?");
						log.log("start");
						input = "";
						repaint();
					}
					resetTimer();
				}
				if (event.getKeyCode() == KeyEvent.VK_BACK_SPACE) {
					userDelete();
					while (!input.equals("")) {
						userDelete();
					}
					resetTimer();
				}
			}
		});

		resetTimer();
		repaint();
	}
	
	private ArrayList<Integer> calnGroupSize(HashMap<Character, Character> groupsKey) {
		/**
		 * Transform the method to express groups of letter from general keyboard to scanning keyboard.
		 */
		ArrayList<Integer> groupSize = new ArrayList<>();
		int key = 0;
		for (int i = 1; i < 26; i++) {
			char ch = (char)('a' + i);
			if (groupsKey.get(ch) == ch) {
				groupSize.add(i - key);
				key = i;
			}
		}
		groupSize.add(26 - key);
		return groupSize;
	}
	
	private void repaint() {
		/**
		 * Group according to the latest user input and repaint the keyboard.
		 */
		keyboard.Grouping();
		ArrayList<Integer> groupSize = calnGroupSize(keyboard.GetGroupsKey());
		
		//Update the uncompleted word of its suffix. 
		if (input == null) {
			input = "";
		}
		input = deleteLastWord(input);
		if (input.length() > 0) {
			input = input + " ";
		}
		input = input + dictionary.GetMaxLikelihoodString();
		
		ArrayList<String> words = dictionary.GetRankedWords();
		simulator.reset(groupSize, phrases.getPhrase(), input, words);
	}
	
	private void resetTimer() {
		/**
		 * Call when the user press a button.
		 */
		if (timer != null) {
			timer.cancel();
		}
		timer = new Timer();
		timer.scheduleAtFixedRate(new TimerTask() {
			@Override
			public void run() {
				//Reject when time out.
				simulator.reject();
			}
		}, DWELL_TIME, DWELL_TIME);
	}
	
	private String deleteLastWord(String str) {
		//Delete the last word divided by space.
		int lastSpace = -1;
		for (int i = input.length() - 1; i >= 0; i--) {
			if (input.charAt(i) == ' ') {
				lastSpace = i;
				break;
			}
		}
		if (lastSpace == -1) {
			return "";
		} else {
			return str.substring(0, lastSpace);
		}
	}
	
	public static void main(String[] args) {
		new ScanningSimulator();
	}

	public void userSelectWord(int id) {
		ArrayList<String> words = dictionary.GetRankedWords();
		dictionary.ClearGroups();
		String word = words.get(id);
		log.log("select " + word + " " + (id + 1));
		log.log("start");
		
		input = deleteLastWord(input);
		if (input.length() > 0) {
			input = input + " ";
		}
		input = input + word + " ";
		
		repaint();
	}

	public void userDelete() {
		log.log("delete");
		if (dictionary.GetInputLength() == 0) {
			input = deleteLastWord(input);
			log.log("start");
		} else {
			dictionary.DeleteGroup();
		}
		repaint();
	}

	public void userSelectGroup(HashSet<Character> group) {
		dictionary.InputGroup(group);
		repaint();
	}
}
